﻿// -----------------------------------------------------------------------
// <copyright file="ImageArrangerViewModel.cs" company="Christoph van der Fecht">
// This code can be used in Comercial, free and opensource projects
// </copyright>
// -----------------------------------------------------------------------

namespace MediaTool.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;

    /// <summary>
    /// View model for the image arranger feature.
    /// </summary>
    public class ImageArrangerViewModel : ViewModelBase
    {
        /// <summary>
        /// Name of the <see cref="SourceFolder"/> property.
        /// </summary>
        private const string SourcePathName = "SourceFolder";

        /// <summary>
        /// Name of the <see cref="PictureName"/> property.
        /// </summary>
        private const string PictureNameName = "PictureName";

        /// <summary>
        /// name of the <see cref="Pictures"/> property.
        /// </summary>
        private const string PicturesName = "Pictures";

        /// <summary>
        /// Name of the <see cref="Progrss"/> property.
        /// </summary>
        private const string ProgressName = "Progress";

        /// <summary>
        /// Name of the <see cref="ProgressMessage"/> property.
        /// </summary>
        private const string ProgressMessageName = "ProgressMessage";

        /// <summary>
        /// Name of the <see cref="ProgressInfo"/> property.
        /// </summary>
        private const string ProgressInfoName = "ProgressInfo";

        /// <summary>
        /// Name of the <see cref="IsStartPossibel"/> property.
        /// </summary>
        private const string IsStartPossibelName = "IsStartPossibel";

        /// <summary>
        /// Message to enter a file name.
        /// </summary>
        private const string DefaultFileName = "Plaese enter name for the pictures...";

        /// <summary>
        /// Pictures that are used for the rearrange.
        /// </summary>
        private ObservableCollection<Model.Picture.JPGImage> pictures = new ObservableCollection<Model.Picture.JPGImage>();

        /// <summary>
        /// Path all pictures are stored.
        /// </summary>
        private string sourceFolder = string.Empty;

        /// <summary>
        /// Name of the pictures.
        /// </summary>
        private string picturename = string.Empty;

        /// <summary>
        /// The progress of the processed pictures.
        /// </summary>
        private double progress = 0.0;

        /// <summary>
        /// Stepping for the progress bar.
        /// </summary>
        private double progressStrepping = 0.0;

        /// <summary>
        /// Information text for the progress bar.
        /// </summary>
        private string progressInfo = string.Empty;

        /// <summary>
        /// Message for the progressbar.
        /// </summary>
        private string progressMessage = string.Empty;

        /// <summary>
        /// Value indicating whether the starting of the rearrangeing is allowed or not.
        /// </summary>
        private bool isStartPossible = false;

        /// <summary>
        /// Thread to load all images.
        /// </summary>
        private System.Threading.Thread processThread = null;

        #region private Commands

        /// <summary>
        /// Command to start the rearranging of all pictures.
        /// </summary>
        private RelayCommand reArrangePicturesCommand = null;

        /// <summary>
        /// Command to open the Browse Dialog.
        /// </summary>
        private RelayCommand browseImagesCommand = null;
        
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageArrangerViewModel"/> class.
        /// </summary>
        public ImageArrangerViewModel()
        {
            if (this.IsInDesignMode)
            {
                this.SourceFolder = "Just for design a path to be displayed.";
                this.PictureName = "Dummy Picture name";
                this.Progress = 50.0;
                this.ProgressInfo = "Desing information text...";
            }
            else
            {
                this.SourceFolder = "Please choos a folder...";
                this.PictureName = DefaultFileName;
                this.Progress = 0.0;
                this.IsStartPossibel = false;

                this.UpdateProgress += OnUpdateProcess;
            }
        }

        /// <summary>
        /// Delegate for the <see cref="UpdateProgress"/> event.
        /// </summary>
        /// <param name="sender">Object that raised the event.</param>
        /// <param name="percent">Percentage value.</param>
        public delegate void UpdateProgressDelegate(object sender, double percent);

        /// <summary>
        /// Occures when the value of the progressbar changed.
        /// </summary>
        public event UpdateProgressDelegate UpdateProgress;

        /// <summary>
        /// Gets or sets the path where all pictures are stored.
        /// </summary>
        public string SourceFolder
        {
            get
            {
                return this.sourceFolder;
            }
            set
            {
                if (value == this.sourceFolder)
                {
                    return;
                }

                this.sourceFolder = value;
                this.RaisePropertyChanged(SourcePathName);
            }
        }

        /// <summary>
        /// Gets or sets the name of the pictures.
        /// </summary>
        public string PictureName
        {
            get
            {
                return this.picturename;
            }
            set
            {
                if (value == this.picturename)
                {
                    return;
                }

                this.picturename = value.Trim();
                this.RaisePropertyChanged(PictureNameName);
            }
        }

        /// <summary>
        /// Gets the progress of the processed pictures.
        /// </summary>
        public double Progress
        {
            get
            {
                return this.progress;
            }

            private set
            {
                if (value == this.progress)
                {
                    return;
                }

                this.progress = value;
                this.RaisePropertyChanged(ProgressName);
                this.ProgressMessage = value.ToString("0.##");
            }
        }

        /// <summary>
        /// Gets or sets the message for the progressbar.
        /// </summary>
        public string ProgressMessage
        {
            get
            {
                return this.progressMessage;
            }

            set
            {
                if (value == this.progressMessage)
                {
                    return;
                }

                this.progressMessage = string.Format("{0}%", value);
                this.RaisePropertyChanged(ProgressMessageName);
            }
        }

        /// <summary>
        /// Gets the information text for the progress bar.
        /// </summary>
        public string ProgressInfo
        {
            get
            {
                return this.progressInfo;
            }

            private set
            {
                if (value == this.progressInfo)
                {
                    return;
                }

                this.progressInfo = value;
                this.RaisePropertyChanged(ProgressInfoName);
            }
        }

        /// <summary>
        /// Gets or sets a list of pictures that need to be rearranged.
        /// </summary>
        public ObservableCollection<Model.Picture.JPGImage> Pictures
        {
            get
            {
                return this.pictures;
            }
            set
            {
                if (value == this.pictures)
                {
                    return;
                }

                this.pictures = value;
                this.RaisePropertyChanged(PicturesName);
            }
        }

        /// <summary>
        /// Gets a value indicating whether the starting of the rearrangeing is allowd or not.
        /// </summary>
        public bool IsStartPossibel
        {
            get
            {
                return this.isStartPossible;
            }

            private set
            {
                if (value == this.isStartPossible)
                {
                    return;
                }

                this.isStartPossible = value;
                this.RaisePropertyChanged(IsStartPossibelName);
            }
        }

        /// <summary>
        /// Gets the command to start the rearrange of all pictures.
        /// </summary>
        public RelayCommand ReArrangePicturesCommand
        {
            get
            {
                if (this.reArrangePicturesCommand == null)
                {
                    this.reArrangePicturesCommand = new RelayCommand(this.StartRearrangePictures);
                }

                return this.reArrangePicturesCommand;
            }
        }

        /// <summary>
        /// Gets the command to browse the images.
        /// </summary>
        public RelayCommand BrowseImagesCommand
        {
            get
            {
                if (this.browseImagesCommand == null)
                {
                    this.browseImagesCommand = new RelayCommand(this.BrowseImages);
                }

                return this.browseImagesCommand;
            }
        }

        /// <summary>
        /// starts the thread to load all images.
        /// </summary>
        public void StartLoadigImages()
        {
            if (this.processThread != null)
            {
                this.processThread.Abort();
                this.processThread = null;
            }

            this.ProgressInfo = "Loading images...";
            this.processThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.LoadPictures));
            this.processThread.Start();
        }

        /// <summary>
        /// Cleans all used resources.
        /// </summary>
        public override void Cleanup()
        {
            this.sourceFolder = string.Empty;
            this.picturename = string.Empty;
            this.pictures = null;
            this.progress = 0.0;
            this.progressMessage = string.Empty;
            this.progressStrepping = 0.0;
            this.reArrangePicturesCommand = null;

            base.Cleanup();
        }

        /// <summary>
        /// Loads all pictures from the defined <see cref="SourceFolder"/>.
        /// </summary>
        private void LoadPictures()
        {
            if (string.IsNullOrEmpty(this.SourceFolder))
            {
                return;
            }

            this.IsStartPossibel = false;
            this.ResetView();

            System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(this.SourceFolder);
            if (!dirInfo.Exists)
            {
                return;
            }

            // reset the progress.
            if (this.Progress > 0.0)
            {
                this.Progress = 0.0;
            }

            var files = dirInfo.GetFiles("*.jpg");
            this.progressStrepping = 100.0 / files.Length;
            List<Model.Picture.JPGImage> pics = new List<Model.Picture.JPGImage>();

            
            foreach (System.IO.FileInfo info in files)
            {
                Model.Picture.JPGImage image = new Model.Picture.JPGImage(info.FullName);
                image.ReadDateTaken();
                pics.Add(image);

                this.RaiseUpdateProgress(this.progressStrepping);
            }
            files = null;
            this.Pictures = new ObservableCollection<Model.Picture.JPGImage>(pics);
            this.ProgressInfo = string.Empty;
            this.IsStartPossibel = true;
        }

        /// <summary>
        /// Starts the rearraning of all found pictures.
        /// </summary>
        private void StartRearrangePictures()
        {
            if (this.processThread != null)
            {
                this.processThread.Abort();
                this.processThread = null;
            }

            this.Progress = 0.0;
            this.processThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.RearrangePictures));
            this.processThread.Start();
        }

        /// <summary>
        /// Sorts all pictures by the Datetaken property and renames them
        /// by using the <see cref="Picturename"/> property.
        /// </summary>
        private void RearrangePictures()
        {
            this.ProgressMessage = "Rearranging and renaming files...";

            List<Model.Picture.JPGImage> pictures = new List<Model.Picture.JPGImage>(this.Pictures);
            pictures.Sort();
            this.Pictures.Clear();

            int pictureCounter = 1;
            foreach (Model.Picture.JPGImage pic in pictures)
            {
                string padding = string.Empty;

                if (pictures.Count < 10)
                {
                    padding = "D1";
                }
                else if (pictures.Count < 100)
                {
                    padding = "D2";
                }
                else if (pictures.Count < 1000)
                {
                    padding = "D3";
                }

                System.IO.FileInfo info = new System.IO.FileInfo(pic.PicturePath);
                string destinationPath = string.Format(@"{0}\{1} {2}{3}", info.DirectoryName, this.PictureName, pictureCounter.ToString(padding), info.Extension);
                info.MoveTo(destinationPath);
                pic.PicturePath = destinationPath;
                pictureCounter++;

                this.RaiseUpdateProgress(this.progressStrepping);
                this.ProgressInfo = string.Empty;
            }

            this.Pictures = new ObservableCollection<Model.Picture.JPGImage>(pictures);
        }

        /// <summary>
        /// Resets the view.
        /// </summary>
        private void ResetView()
        {
            // reset the picture collection.
            if (this.Pictures.Count > 0)
            {
                this.Pictures.Clear();
            }

            // reset previews progress.
            if (this.Progress > 0.0)
            {
                this.Progress = 0.0;
            }

            // insert default message to enter a file name.
            if (!this.PictureName.Equals(DefaultFileName))
            {
                this.PictureName = DefaultFileName;
            }
        }

        /// <summary>
        /// Handles all updates about the current progress.
        /// </summary>
        /// <param name="sender">Obejct that raised the event.</param>
        /// <param name="percent">Percentage value.</param>
        private void OnUpdateProcess(object sender, double percent)
        {
            if (percent == 0.0)
            {
                this.Progress = 0.0;
            }

            this.Progress += percent;
        }

        /// <summary>
        /// raises the <see cref="UpdateProgress"/> event.
        /// </summary>
        private void RaiseUpdateProgress(double percentage)
        {
            if (this.UpdateProgress != null)
            {
                this.UpdateProgress(this, percentage);
            }
        }

        /// <summary>
        /// Shows a brwose daialog to select the images.
        /// </summary>
        private void BrowseImages()
        {
            FolderBrowserDialog folderdialog = new FolderBrowserDialog();
            folderdialog.RootFolder = Environment.SpecialFolder.MyPictures;

            if (folderdialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.SourceFolder = folderdialog.SelectedPath;
                this.StartLoadigImages();
            }
        }
    }
}
